﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using SUMO.MediaBrowser.Foundation;
using SUMO.MediaBrowser.ViewModel;
using USTF.Media.MediaService;

namespace SUMO.MediaBrowser.ViewModel
{
    /// <summary>
    /// Store for a group of modifications which are awaiting save. Can also be used to monitor a group of items for changes which, if ever changed, will be added to the list of items awaiting persistance.
    /// If they are ever changed to not be marked as "modified" anymore, they will be removed from the list of items pending peristance.
    /// </summary>
    public class PendingModifications : INotifyPropertyChanged
    {
        private BackgroundWorker _modificationSaver = null;

        /// <summary>
        /// Initialises a new store of modifications and ability to change those isolated modifications to the server
        /// </summary>
        public PendingModifications()
        {
        }


        /// <summary>
        /// Retreives the Application Singleton (static) instance of the PendingModifications object
        /// </summary>
        public static PendingModifications Current
        {
            get
            {
                return Singleton<PendingModifications>.Instance;
            }
        }

        /// <summary>
        /// Items modified accross the application awaiting save. Child items are ignored as they will be added to this flat list on modification.
        /// </summary>
        public ObservableCollection<HierarchicalMediaItem> ModifiedItems = new ObservableCollection<HierarchicalMediaItem>();

        /// <summary>
        /// Items currently in the queue for persisting to server
        /// </summary>
        public ObservableCollection<HierarchicalMediaItem> ItemsBeingSaved = new ObservableCollection<HierarchicalMediaItem>();

        /// <summary>
        /// Items which, when attempted to be saved, have generated an error message
        /// </summary>
        public ObservableCollection<FailedSave> SaveFailures = new ObservableCollection<FailedSave>();

        /// <summary>
        /// Start monitoring the properties of a media item's data. If actual media data has changed, the item will be queued for persiting to server.
        /// </summary>
        /// <param name="dataItem"></param>
        public void RegisterForChangeMonitoring(HierarchicalMediaItem dataItem)
        {
            //hook in to INotifyPropertyChanged event handler for ViewModel
            dataItem.PropertyChanged += (object sender, PropertyChangedEventArgs e)
                =>
                {
                    var modifiedDataItem = sender as HierarchicalMediaItem;
                    if (modifiedDataItem != null)
                    {
                        //only add to list if:
                        //  1: the byte[] data has changed
                        //  2: it's actually changed (rather than loading the original)
                        //  3: we don't already have it
                        if (e.PropertyName.Equals("DataModified", StringComparison.InvariantCultureIgnoreCase))
                        {
                            if (modifiedDataItem.DataModified)
                            {
                                //data has been modified, add to list if we don't already have it
                                if (!ModifiedItems.Contains(modifiedDataItem))
                                    ModifiedItems.Add(modifiedDataItem);
                            }
                            else
                            {
                                //data has been marked as 'not modified.' remove from list if we have it
                                //(maybe it was reverted)
                                if (ModifiedItems.Contains(modifiedDataItem))
                                    ModifiedItems.Remove(modifiedDataItem);
                            }

                        }
                    }
                };
        }

        /// <summary>
        /// Gets a property indicating whether a save operation is in progress
        /// </summary>
        public bool IsSaving
        {
            get { return _isSaving; }
            private set
            {
                if (value == _isSaving)
                    return;
                _isSaving = value;
                if (IsCancelling)
                    IsCancelling = false;
                OnPropertyChanged("IsSaving");
            }
        }
        private bool _isSaving = false;


        /// <summary>
        /// Gets or Sets a property indicating whether a cancel operation is or should be in progress
        /// </summary>
        public bool IsCancelling
        {
            get { return _isCancelling; }
            private set
            {
                if (value == _isCancelling)
                    return;
                _isCancelling = value;
                OnPropertyChanged("IsCancelling");
            }
        }
        private bool _isCancelling = false;

        #region INotifyPropertyChanged Members
        /// <summary>
        /// Occurs when any property, apart from ObservableCollection<>s change
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        /// <summary>
        /// Instructs any in-progress asynchronous Save operations to end at the next opportunity.
        /// </summary>
        public void CancelAsync()
        {
            //if we've nothing to cancel, or we're already cancelling then don't do anything
            if (IsCancelling || !IsSaving)
                return;

            //set the property to show observers that it's happening
            IsCancelling = true;

            //tell the saving thread to cancel
            if (_modificationSaver != null)
                _modificationSaver.CancelAsync();
        }

        /// <summary>
        /// Begins an operation where the buffer of items awaiting persistence to the server are sent to the server for save.
        /// This operation can be cancelled by calling the CancelAsync method.
        /// The status can be tracked by monitoring the IsSaving, IsCancelling properties, and the ModifiedItems ObservableCollection.
        /// </summary>
        public void SaveAsync()
        {
            if (IsSaving)
                return;

            //order by parents -> children
            //note: we don't follow the hierarchy of HierarchicalMediaItems recursively
            //      since if anything has been modified, the item's reference will be directly 
            //      added to our "ModifiedItems" collection

            SaveFirstItemAndContinue();
        }

        private void SaveFirstItemAndContinue()
        {
            // should we continue or stop?
            if (IsCancelling || ModifiedItems.Count <= 0)
            {
                IsSaving = false;
                return;
            }

            //deal with first item in list
            var itemToSave = ModifiedItems[0];

            //get a server proxy
            var serviceProxy = OpsContext.Current.NewClient();

            //when item is saved, move on to next
            serviceProxy.SaveDataCompleted += (object sender, AsyncCompletedEventArgs e) =>
                {
                    
                    //if fail, move to failure list with message
                    if (e.Error != null)
                        SaveFailures.Add(new FailedSave { ItemToSave = ModifiedItems[0], ErrorMessage = e.Error.Message });

                    ModifiedItems[0].IsBeingUploaded = false;

                    //remove the first item
                    ModifiedItems[0].DataModified = false;

                    //start the async process of saving the next first item
                    SaveFirstItemAndContinue();
                    
                };
            
            //begin save of first available item
            itemToSave.IsBeingUploaded = true;
            var metadataToSave = itemToSave.CurrentItem;

            if (itemToSave.CurrentItem is NewMediaItem)
            {
                metadataToSave = new MediaItem
                {
                    ID = itemToSave.CurrentItem.ID,
                    MediaType = itemToSave.CurrentItem.MediaType,
                    Properties = itemToSave.CurrentItem.Properties
                };
            }
            serviceProxy.SaveDataAsync(metadataToSave, String.Empty, itemToSave.ItemData);
        }

   



    }
}
